package alocador.gerenciadores;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import alocador.entidades.salas.Classificacao;
import alocador.entidades.salas.SalaConcreta;
import alocador.exceptions.FinalidadeInvalidaException;
import alocador.exceptions.SalaInvalidaException;
import alocador.exceptions.SalaNaoExistenteException;
import alocador.persistencia.PersistenciaFacade;
import alocador.persistencia.Gravavel;
import alocador.persistencia.SalasFileManager;
import alocador.util.VerificaInvalido;

/**
 * Classe que ira Gerenciar as Salas do Sistema
 */
public class GerenciadorDeSalas implements Gravavel {

	private static GerenciadorDeSalas instancia = null;
	private List<Classificacao> classificacoes = null;
	private ArrayList<SalaConcreta> salas = null;
	private static String fileName = "GerSalas";

	/**
	 * Metodo que implementa o Padrao Singleton para o Gerenciador
	 * 
	 * @return Instancia Unica do Gerenciador
	 */
	public static GerenciadorDeSalas getInstance() {

		if (instancia == null) {
			GerenciadorDeSalas gds = (GerenciadorDeSalas) PersistenciaFacade
					.recuperarArquivo(fileName);

			if (gds == null)
				gds = new GerenciadorDeSalas();

			instancia = gds;
		}
		return instancia;
	}

	/**
	 * Construtor da Classe Cria um Gerenciador sem nenhuma sala armazenada
	 */

	private GerenciadorDeSalas() {
		zerarDadosInternos();
	}

	/**
	 * Metodo para adicionar uma nova sala ao Sistema
	 * 
	 * @param id
	 *            Id da Sala
	 * @param capacidade
	 *            Quantidade de Pessoas que cabem na sala
	 * @param finalidade
	 *            Finalidade da Sala
	 * @param tipo
	 *            Tipo da Sala
	 * @throws Exception
	 */
	public void adicionaSala(String id, int capacidade, String finalidade,
			String tipo) throws Exception {

		adicionaSala(id, capacidade, finalidade, tipo, "", true);
	}

	/**
	 * Metodo para adicionar uma nova sala ao Sistema
	 * 
	 * @param id
	 *            Id da Sala
	 * @param capacidade
	 *            Quantidade de Pessoas que cabem na sala
	 * @param finalidade
	 *            Finalidade da Sala
	 * @param tipo
	 *            Tipo da Sala
	 * @param apelido
	 *            Apelido para a Sala
	 * @throws Exception
	 */
	public void adicionaSala(String id, int capacidade, String finalidade,
			String tipo, String apelido) throws Exception {

		adicionaSala(id, capacidade, finalidade, tipo, apelido, true);
	}

	/**
	 * Metodo para adicionar uma nova sala ao Sistema
	 * 
	 * @param id
	 *            Id da Sala
	 * @param capacidade
	 *            Quantidade de Pessoas que cabem na sala
	 * @param finalidade
	 *            Finalidade da Sala
	 * @param tipo
	 *            Tipo da Sala
	 * @param apelido
	 *            Apelido para a Sala
	 * @param aberto
	 *            True - Sala Aberta (nao pode ser alocada)<br>
	 *            False - Sala Fechada (pode ser alocada)
	 * @throws Exception
	 */
	public void adicionaSala(String id, int capacidade, String finalidade,
			String tipo, String apelido, boolean aberto) throws Exception {

		if (VerificaInvalido.basico(finalidade))
			throw new FinalidadeInvalidaException("Finalidade invalida.");

		SalaConcreta novaSala = null;

		// Excecao Apenas para Escritorio, que Recebe Apelido no parametro do
		// Tipo
		if (finalidade.equals("Escritorio"))
			novaSala = criaSala(id, capacidade, finalidade, "", tipo, aberto);
		else
			novaSala = criaSala(id, capacidade, finalidade, tipo, apelido,
					aberto);

		adicionaSala(novaSala);
	}

	// Cria e Retorna sala Dependendo dos Atributos passados

	private SalaConcreta criaSala(String id, int capacidade, String finalidade,
			String tipo, String apelido, boolean aberto) throws Exception {

		for (Classificacao c : classificacoes) {
			if (c.getID().equalsIgnoreCase(finalidade)) {
				return c.criaSala(id, capacidade, apelido, tipo, "SerAberta:"
						+ aberto);
			}
		}

		throw new SalaInvalidaException("Finalidade nao existente: "
				+ finalidade);
	}

	private void adicionaSala(SalaConcreta novaSala)
			throws SalaInvalidaException {

		Iterator<SalaConcreta> it = salas.iterator();

		while (it.hasNext()) {
			SalaConcreta s = it.next();
			if (s.getNome().equals(novaSala.getNome()))
				throw new SalaInvalidaException(
						"Ja existe sala com esta identificacao.");
		}

		salas.add(novaSala);
	}

	/**
	 * Metodo que localiza uma sala do gerenciador pelo ID
	 * 
	 * @param id
	 *            ID da sala a ser Localizada
	 * @return Sala Localizada
	 * @throws SalaNaoExistenteException
	 *             Caso a sala nao exista
	 */
	public SalaConcreta getSala(String id) throws SalaNaoExistenteException {
		Iterator<SalaConcreta> it = salas.iterator();
		while (it.hasNext()) {
			SalaConcreta s = it.next();
			if (s.getNome().equals(id))
				return s;
		}
		throw new SalaNaoExistenteException("Sala nao existe.");
	}

	/**
	 * Remove sala baseada em um ID passado
	 * 
	 * @param id
	 *            ID da sala a ser removida
	 * @throws SalaNaoExistenteException
	 *             Caso a sala nao exista
	 */
	public void removeSala(String id) throws SalaNaoExistenteException {
		Iterator<SalaConcreta> it = salas.iterator();
		while (it.hasNext()) {
			SalaConcreta s = it.next();
			if (s.getNome().equals(id)) {
				removeSala(s);
				return;
			}
		}
		throw new SalaNaoExistenteException("Sala nao existe.");
	}

	private void removeSala(SalaConcreta s) {
		salas.remove(s);
	}

	@Override
	public void zerarDadosInternos() {
		salas = new ArrayList<SalaConcreta>();
		classificacoes = createClassificacoes();
	}

	// Cria as Classificacoes

	private List<Classificacao> createClassificacoes() {
		try {
			return SalasFileManager.carregaClassificacoes();

		} catch (IOException e) {
			System.err.print("Erro ao carregar Salas: " + e.getMessage());
		}
		return new ArrayList<Classificacao>();
	}

	@Override
	public String getFileName() {
		return GerenciadorDeSalas.fileName;
	}

}
